package co.edu.ucatolica.sislab.persistence;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import co.edu.ucatolica.sislab.persistence.utils.DAOFactory;
import co.edu.ucatolica.sislab.persistence.utils.DatabaseOperations;
import co.edu.ucatolica.sislab.persistence.utils.DatabaseTypes;
import co.edu.ucatolica.sislab.persistence.utils.PersistenceUtils;
import co.edu.ucatolica.sislab.persistence.utils.SQLGeneralLoader;
import co.edu.ucatolica.sislab.persistence.utils.SQLSISLABDBConstants;
import co.edu.ucatolica.sislab.utils.DatabaseTables;

public class DatabasePersistorHelper {
	/**
	 * Logger asociado
	 */
	private static Logger logger = LoggerFactory.getLogger(DatabasePersistorHelper.class);
	
	/**
	 * Numero de transaccion
	 */
	private static long nextTransaction = 0; 
	
	/**
	 * SQL disponibles.
	 */
	private static SQLGeneralLoader loader = SQLGeneralLoader.getSingleton();
	
	/**
	 * Hash con las conexiones dadas para cada transaccion
	 */
	private static HashMap<Long, Connection> databaseConnections = new HashMap<Long, Connection>(); 
	
	/**
	 * Recupera un sql para la base de datos de Base de datos
	 * @param sqlKey Llave a buscar
	 * @return SQL correspondiente a la llave o null de lo contrario
	 */
	public static String getSql( String sqlKey ) {
		return loader.getSqls(DatabaseTypes.SISLABDB, sqlKey );
	}
	
	public synchronized static long  getNextTransaction() {
		nextTransaction++;
		return nextTransaction;
	}
	
	/**
	 * Construye una conexion a la base de datos
	 * @return Conexion a la base de datos
	 */
	public static Connection getConexion( ) {		
		DAOFactory factory = DAOFactory.getInstancia();
		Connection conexionBaseDatos = factory.getConexion(DatabaseTypes.SISLABDB);
		
		return conexionBaseDatos;
	}
	
	/**
	 * Construye una conexion a la base de datos utilizando el hash para no solicitar
	 * conexion si ya hay una transaccion asociada.
	 * @return Conexion a la base de datos con el modo autocommit en false
	 */
	public static Connection getConexion( long transactionNumber ) throws DatabaseException {
		if ( databaseConnections.containsKey(transactionNumber)) {
			return databaseConnections.get(transactionNumber);
		} else {
			DAOFactory factory = DAOFactory.getInstancia();
			Connection conexionBaseDatos = factory.getConexion(DatabaseTypes.SISLABDB);
			try {
				conexionBaseDatos.setAutoCommit(false);
			} catch (SQLException e) {
				throw new DatabaseException(DatabaseTables.GENERIC, DatabaseOperations.OPEN_CONNECTION, e.toString());
			}
			databaseConnections.put(transactionNumber, conexionBaseDatos);	
			return conexionBaseDatos;
		}
	}
	
	/**
	 * Cierra la conexion asociada a una transaccion si existe, si no no hace ninguna operacion
	 * @param transactionNumber Numero de transaccion
	 */
	public static void closeConnection( long transactionNumber ) throws DatabaseException {
		if ( databaseConnections.containsKey(transactionNumber)) {
			Connection conn = databaseConnections.get(transactionNumber);
			if ( conn != null ) {
				try {
					DAOFactory.getInstancia().cerrarConexionServicio(conn);
				} catch ( SQLException e ) {
					logger.error( "Error cerrando la conexion " + e.toString() );
					throw new DatabaseException(DatabaseTables.GENERIC, DatabaseOperations.CLOSE_CONNECTION, e.toString());
				}
			}			
		} 
	}
	
	/**
	 * Realiza el commit para la transaccion indicada
	 */
	public static void commit( long transactionNumber ) throws DatabaseException {
		Connection conn = databaseConnections.get(transactionNumber);
		if ( conn != null ) {
			try {
				conn.commit();
			} catch (SQLException e) {
				throw new DatabaseException(DatabaseTables.GENERIC, DatabaseOperations.COMMIT, e.toString());
			}
		}		
	}
	
	/**
	 * Realiza el rollback para la transaccion indicada
	 */
	public static void rollback( long transactionNumber ) throws DatabaseException  {
		Connection conn = databaseConnections.get(transactionNumber);
		if ( conn != null ) {
			try {
				conn.rollback();
			} catch (SQLException e) {
				logger.error ( "Excepcion SQL " + e.toString() );
				throw new DatabaseException(DatabaseTables.GENERIC, DatabaseOperations.ROLLBACK, e.toString());
			}
		}		
	}
	
	/**
	 * Retorna la llave generada por la tabla a la que se le acaba de insertar un registro con 
	 * la conexion recibida por parametro 
	 * @param conn
	 * @return Long
	 */
	public static Long getIdentity( Connection conn ) { // throws BasedatosException {
		
		Long answer = null;
		String sql = getSql(SQLSISLABDBConstants.SQL_IDENTITY);
		PreparedStatement ps = null;
		ResultSet resultsetRta = null;
		try {
			if ((conn != null) && (sql != null)) {
				ps = conn.prepareStatement(sql);
				resultsetRta = ps.executeQuery();		    	
				
		    	if ( resultsetRta.next() ) {
		        	answer = resultsetRta.getLong(1);
		        	logger.info("answer " + answer);
		        }
			}
		}  catch (SQLException e) {
			logger.error(" " + e);
			
		} finally {
			PersistenceUtils.closeConnectionIssues( ps, resultsetRta );
		}
		return answer;			
	}
	
	/**
	 * Retorna la llave generada por la tabla a la que se le acaba de insertar un registro con 
	 * la conexion recibida por parametro 
	 * @param conn
	 * @return Long
	 */
	public static Long getIdentity( long transactionNumber  ) throws DatabaseException {
		
		Long answer = null;
		String sql = getSql(SQLSISLABDBConstants.SQL_IDENTITY);
		PreparedStatement ps = null;
		ResultSet resultsetRta = null;
		Connection conn = DatabasePersistorHelper.getConexion(transactionNumber);
		try {
			if ((conn != null) && (sql != null)) {
				ps = conn.prepareStatement(sql);
				resultsetRta = ps.executeQuery();		    	
				
		    	if ( resultsetRta.next() ) {
		        	answer = resultsetRta.getLong(1);
		        	logger.info("answer " + answer);
		        }
			}
		}  catch (SQLException e) {
			logger.error(" " + e);
			throw new DatabaseException(DatabaseTables.GENERIC, DatabaseOperations.IDENTITY, e.toString());
		} finally {
			PersistenceUtils.closeConnectionIssues( ps, resultsetRta );
		}
		return answer;			
	}
}
